#include<android/log.h>
#include <jni.h>
#include <string>
#include "log.h"
#include "FFmpegWrapper.h"


extern "C" {
#include "libavutil/log.h"
#include "libavformat/avformat.h"
#include "libavutil/dict.h"
#include "libavcodec/avcodec.h"
#include "libavutil/timestamp.h"
#include "libavutil/opt.h"
}

static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag) {
    AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;

    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
           tag,
           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
           av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
           pkt->stream_index);
}


extern "C"
JNIEXPORT jint JNICALL
Java_com_example_helloffmpeg_system_FfmpegHelper_encodeAudio(JNIEnv *env, jobject thiz,jstring dst_file_path) {

    const char *outfilename = env->GetStringUTFChars(dst_file_path, nullptr);
    LOGD("dst_file_path: %s\n",outfilename);

    const AVCodec *codec = nullptr;
    AVCodecContext *avCodecContext = nullptr;
    int i, ret, x, y;
    FILE *file = nullptr;
    AVFrame *frame = nullptr;
    AVPacket *pkt = nullptr;
    uint8_t endcode[] = {0, 0, 1, 0xb7};

    //1.create codec
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);

    if (!codec) {
        LOGD("Could not avcodec_find_encoder\n");
        goto end;
    }

    avCodecContext = avcodec_alloc_context3(codec);
    if (!avCodecContext) {
        LOGD("avcodec_alloc_context3\n");
        goto end;
    }

    pkt = av_packet_alloc();
    if (!pkt) {
        LOGD("av_packet_alloc() failed\n");
        goto end;
    }

    avCodecContext->bit_rate = 400000;
    avCodecContext->width = 352;
    avCodecContext->bit_rate = 288;


    end:
    env->ReleaseStringUTFChars(dst_file_path, outfilename);
    if (file) fclose(file);
    if (avCodecContext) avcodec_free_context(&avCodecContext);
    if (frame) av_frame_free(&frame);
    if (pkt) av_packet_free(&pkt);

    return 0;
}

static void encode(AVCodecContext *avCodecContext, AVFrame *frame, AVPacket *pkt, FILE *outfile) {
    int ret;

    if (frame) LOGD("Send frame %lld\n", frame->pts);

    /* send the frame to the encoder */
    ret = avcodec_send_frame(avCodecContext, frame);
    if (ret < 0) {
        LOGD("Error sending a frame for encoding\n");
        return;
    }

    while (ret >= 0) {
        ret = avcodec_receive_packet(avCodecContext, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            LOGD( "Error during encoding\n");
            return;
        }

        LOGD("Write packet %lli (size=%5d)\n",pkt->pts, pkt->size);
        fwrite(pkt->data, 1, pkt->size, outfile);
        av_packet_unref(pkt);
    }
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_helloffmpeg_system_FfmpegHelper_encodeVideo(JNIEnv *env, jobject thiz,
                                                             jstring src_file_path,
                                                             jstring dst_file_path) {
    const char *outfilename = env->GetStringUTFChars(dst_file_path, nullptr);
    const char *intPutfilename = env->GetStringUTFChars(src_file_path, nullptr);
    LOGD("dst_file_path: %s\n", outfilename);  // 打印路径以确保传递正确

    av_log_set_level(AV_LOG_DEBUG);  // 提升日志级别

    const AVCodec *codec = nullptr;
    AVCodecContext *avCodecContext = nullptr;
    int i, ret, x, y;
    FILE *file = nullptr;
    AVFrame *frame = nullptr;
    AVPacket *pkt = nullptr;
    uint8_t endcode[] = {0, 0, 1, 0xb7};

    // 创建编码器
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        LOGD("Could not find encoder\n");
        goto end;
    }

    avCodecContext = avcodec_alloc_context3(codec);
    if (!avCodecContext) {
        LOGD("Could not allocate video codec context\n");
        goto end;
    }

    pkt = av_packet_alloc();
    if (!pkt) {
        LOGD("Could not allocate packet\n");
        goto end;
    }

    // 设置编码器参数
    avCodecContext->bit_rate = 400000;
    avCodecContext->width = 352;
    avCodecContext->height = 288;
    avCodecContext->time_base = {1, 25};
    avCodecContext->framerate = {25, 1};
    avCodecContext->gop_size = 10;
    avCodecContext->max_b_frames = 1;
    avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;

    if (codec->id == AV_CODEC_ID_H264) {
        AVDictionary *opts = nullptr;
        av_dict_set(&opts, "preset", "slow", 0);
        ret = avcodec_open2(avCodecContext, codec, &opts);
        av_dict_free(&opts);
    } else {
        ret = avcodec_open2(avCodecContext, codec, nullptr);
    }

    LOGD("codec name: %s\n", codec->name);

    if (ret < 0) {
        char errbuf[128];
        av_strerror(ret, errbuf, sizeof(errbuf));
        LOGD("Could not open codec: %s\n", errbuf);
        goto end;
    }

    // 打开文件
    file = fopen(outfilename, "wb");
    if (!file) {
        LOGD("Could not open %s\n", outfilename);
        perror("fopen");  // 输出详细的文件打开失败信息
        goto end;
    }

    // 帧到包
    frame = av_frame_alloc();
    if (!frame) {
        LOGD("Could not allocate video frame\n");
        goto end;
    }
    frame->format = avCodecContext->pix_fmt;
    frame->width = avCodecContext->width;
    frame->height = avCodecContext->height;

    ret = av_frame_get_buffer(frame, 32);
    if (ret < 0) {
        LOGD("Could not allocate the video frame data\n");
        goto end;
    }

    // 编码 1 秒的视频
    for (i = 0; i < 25; i++) {
        fflush(stdout);

        ret = av_frame_make_writable(frame);
        if (ret < 0)
            goto end;

        for (y = 0; y < avCodecContext->height; y++) {
            for (x = 0; x < avCodecContext->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }
        for (y = 0; y < avCodecContext->height / 2; y++) {
            for (x = 0; x < avCodecContext->width / 2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }

        frame->pts = i;

        encode(avCodecContext, frame, pkt, file);
    }

    encode(avCodecContext, nullptr, pkt, file);

    if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO)
        fwrite(endcode, 1, sizeof(endcode), file);

    end:
    env->ReleaseStringUTFChars(dst_file_path, outfilename);
    if (file) fclose(file);
    if (avCodecContext) avcodec_free_context(&avCodecContext);
    if (frame) av_frame_free(&frame);
    if (pkt) av_packet_free(&pkt);

    return 0;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_helloffmpeg_system_FfmpegHelper_listAllEncoders(JNIEnv *env, jobject thiz) {
    const AVCodec *codec = nullptr;
    void *i = 0;

    while ((codec = av_codec_iterate(&i))) {
        if (av_codec_is_encoder(codec)) {
            LOGD("Encoder: %s\n", codec->name);
        }
    }
}
